package com.xiguanlezz.cn.common.notify;

import com.xiguanlezz.cn.common.notify.listener.Subscriber;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * @Author：习惯了种种
 * @CreateDate：2024/11/28 19:35
 * @Description：特定事件生产者实现类
 */
@Slf4j
public class DefaultPublisher extends Thread implements EventPublisher {
    // 线程启动标记，用于线程只启动一次
    private volatile boolean initialized = false;
    // 线程是否停止工作
    private volatile boolean shutdown = false;

    // 当前事件生产器关注的事件
    private Class<? extends Event> eventType;

    // 存放事件订阅者的集合
    protected final ConcurrentSkipListSet<Subscriber> subscribers = new ConcurrentSkipListSet<>();

    // 存放事件队列
    private BlockingQueue<Event> queue;
    // 队列最大容量
    private int queueMaxSize = -1;

    // 被处理过的最新事件的序号
    protected volatile Long lastEventSequence = -1L;
    // 原子更新器，用来更新队列中最后一个时间序号
    private static final AtomicReferenceFieldUpdater<DefaultPublisher, Long> UPDATER = AtomicReferenceFieldUpdater
            .newUpdater(DefaultPublisher.class, Long.class, "lastEventSequence");

    private AtomicLong processEventNum = new AtomicLong(0);


    @Override
    public void init(Class<? extends Event> type, int bufferSize) {
        //设置守护线程
        setDaemon(true);
        // 设置线程名称
        setName("nacos.publisher-" + type.getName());
        //设置关注的事件类型
        this.eventType = type;
        // 设置队列最大容量
        this.queueMaxSize = bufferSize;
        if (this.queueMaxSize <= 0) {
            this.queueMaxSize = 2000;
        }
        // 创建事件队列
        this.queue = new ArrayBlockingQueue<>(this.queueMaxSize);
        // 在这里启动了线程，并设置启动标记
        start();
    }

    @Override
    public synchronized void start() {
        if (initialized) {
            return;
        }
        super.start();
        initialized = true;
    }

    @Override
    public void run() {
        openEventHandler();
    }

    // 处理事件队列中事件的方法
    void openEventHandler() {
        try {
            // 事件发布后，没有订阅者消费，这里设置等待60秒，超过就丢弃事件
            int waitTimes = 60;
            // 在循环中判断线程是否停止工作了，然后判断是不是没有订阅者，并且等待时间仍然大于0
            while (!shutdown && !hasSubscriber() && waitTimes > 0) {
                // 只要上面三个条件都满足，就让当前线程睡一会，等待订阅者订阅事件
                TimeUnit.SECONDS.sleep(1);
                waitTimes--;
            }
            while (!shutdown) {
                // 阻塞获取事件
                final Event event = queue.take();
                // 处理事件
                receiveEvent(event);
                // 更新被处理过的最新事件的序号
                UPDATER.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence()));
            }
        } catch (Throwable ex) {
            log.error("Event listener exception : ", ex);
        }
    }

    private void receiveEvent(Event event) {
        // 得到当前处理事件的序号
        final long currentEventSequence = event.sequence();
        // 判断是否没有订阅者，如果仍然没有，那这个事件就被丢弃了
        if (!hasSubscriber()) {
            log.warn("[NotifyCenter] the {} is lost, because there is no subscriber.", event);
            return;
        }
        // 遍历订阅者
        for (Subscriber subscriber : subscribers) {
            // 对比通知事件的唯一标识
            if (!subscriber.scopeMatches(event)) {
                continue;
            }
            // 生产事件往队列中塞值时存在并发情况，可能会出现序号大的先放，此时需要过滤掉序号小的
            if (subscriber.ignoreExpireEvent() && lastEventSequence > currentEventSequence) {
                log.debug("[NotifyCenter] the {} is unacceptable to this subscriber, because had expire", event.getClass());
                continue;
            }
            // 通知订阅者执行回调方法
            notifySubscriber(subscriber, event);
        }
        processEventNum.incrementAndGet();
    }

    // 判断是否有订阅者的方法
    private boolean hasSubscriber() {
        return CollectionUtils.isNotEmpty(subscribers);
    }

    @Override
    public long currentEventSize() {
        return processEventNum.get();
    }

    @Override
    public void addSubscriber(Subscriber subscriber) {
        subscribers.add(subscriber);
    }

    @Override
    public void removeSubscriber(Subscriber subscriber) {
        subscribers.remove(subscriber);
    }

    @Override
    public boolean publish(Event event) {
        // 判断线程是否启动
        checkIsStart();
        // 把事件放到事件队列中，非阻塞方法
        boolean success = this.queue.offer(event);
        // 添加失败则直接由当前线程处理事件
        if (!success) {
            log.warn("Unable to plug in due to interruption, synchronize sending time, event : {}", event);
            receiveEvent(event);
            return true;
        }
        return true;
    }

    void checkIsStart() {
        if (!initialized) {
            throw new IllegalStateException("Publisher does not start");
        }
    }

    @Override
    public void notifySubscriber(Subscriber subscriber, Event event) {
        log.debug("[NotifyCenter] the {} will received by {}", event, subscriber);
        // 执行订阅者的回调方法
        Runnable job = () -> subscriber.onEvent(event);
        Executor executor = subscriber.executor();
        if (executor != null) {
            // 如果定义了就使用订阅者自己的执行器执行回调方法
            executor.execute(job);
        } else {
            try {
                // 没定义就是用当前线程执行回调方法
                job.run();
            } catch (Throwable e) {
                log.error("Event callback exception: ", e);
            }
        }
    }

    @Override
    public void shutdown() throws Exception {
        this.shutdown = true;
        this.queue.clear();
    }
}
